package com.zzyl.nursing.service.impl;

import java.sql.Wrapper;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpException;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;

import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.exception.ServiceException;
import com.zzyl.common.exception.base.BaseException;

import com.zzyl.nursing.domain.dto.DeviceDto;
import com.zzyl.nursing.domain.pojo.Device;
import com.zzyl.nursing.domain.vo.DeviceDetailVo;
import com.zzyl.nursing.domain.vo.ProductVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.DeviceMapper;

import com.zzyl.nursing.service.IDeviceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

/**
 * 设备Service业务层处理
 * 
 * @author alexis
 * @date 2025-10-11
 */
@Service
@Slf4j
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService
{
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private IoTDAClient iotDAClient;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 查询设备
     * 
     * @param id 设备主键
     * @return 设备
     */
    @Override
    public Device selectDeviceById(Long id)
    {
        return deviceMapper.selectById(id);
    }

    /**
     * 查询设备列表
     * 
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device)
    {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增设备
     * 
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device)
    {
        return deviceMapper.insert(device);
    }

    /**
     * 修改设备
     * 
     * @param device 设备
     * @return 结果
     */
    @Override
    public int updateDevice(Device device)
    {
        return deviceMapper.updateById(device);
    }

    /**
     * 批量删除设备
     * 
     * @param ids 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids)
    {
        return deviceMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除设备信息
     * 
     * @param id 设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceById(Long id)
    {
        return deviceMapper.deleteById(id);
    }

    @Override
    public void syncProductList() {
        ListProductsRequest request = new ListProductsRequest();
        ListProductsResponse listProductsResponse = iotDAClient.listProducts(request);
        if (listProductsResponse.getHttpStatusCode() != 200) {
            throw new BaseException("请求iot平台失败");
        }
        List<ProductSummary> products = listProductsResponse.getProducts();
        //卫语句：java中可以将违反规则或者不满足情况的条件先判断
        if(CollUtil.isEmpty(products))
        {
            throw new BaseException("IOT平台没有任何产品，请您及时添加");
        }
        redisTemplate.opsForValue().set(CacheConstants.ALL_PRODUCT_KEY,JSONUtil.toJsonStr(products));
    }

    @Override
    public List<ProductVo> allProduct() {
        String resultJson = redisTemplate.opsForValue().get(CacheConstants.ALL_PRODUCT_KEY);
        List<ProductSummary> ProductSummary = JSONUtil.toList(resultJson, ProductSummary.class);
        if (CollUtil.isNotEmpty(ProductSummary)){
           return ProductSummary.stream()
                    .map(ps -> ProductVo.builder().productKey(ps.getProductId()).productName(ps.getName()).build()).collect(Collectors.toList());
        }
        return List.of();
    }

    @Override
    public void registerDevice(DeviceDto deviceDto) {
        // 1-校验：去表中查询是否存在,校验设备名称,校验设备标识码,校验同一个位置不能绑定同一个产品
        // sql: select count(1) from device where device_name=? or node_id or (location_type=? and binding_location_type=? and physical_location-type=? and product_key=?)

        Integer physicalLocationType = deviceDto.getPhysicalLocationType();
        this.lambdaQuery()
                .eq(Device::getDeviceName, deviceDto.getDeviceName())
                .or().eq(Device::getNodeId, deviceDto.getNodeId())
                .or(wrapper->wrapper
                        .eq(Device::getLocationType, deviceDto.getLocationType())
                        .eq(Device::getBindingLocation, deviceDto.getBindingLocation())
                        .eq(ObjectUtil.isNotNull(physicalLocationType), Device::getPhysicalLocationType, deviceDto.getPhysicalLocationType())
                        .eq(Device::getProductKey, deviceDto.getProductKey()))
                .count();
        if (count() > 1){
            throw new ServiceException("注册失败：请检查设备名称和标识符不能重复、同一个位置不能绑定同一个设备");
        }


        // 2-调用iot平台接口注册设备
        AddDeviceRequest request=new AddDeviceRequest();
        AddDevice body=new AddDevice();
        body.withNodeId(deviceDto.getNodeId());
        body.withProductId(deviceDto.getProductKey());
        body.withDeviceName(deviceDto.getDeviceName());
        request.withBody(body);
        AddDeviceResponse addDeviceResponse=iotDAClient.addDevice(request);
        if (addDeviceResponse.getHttpStatusCode() != 201) {
            throw new ServiceException("iot注册设备失败");
        }
        String deviceId=addDeviceResponse.getDeviceId();
        String secret=addDeviceResponse.getAuthInfo().getSecret();

        // 3-把设备保存到表中
        Device device= BeanUtil.toBean(deviceDto,Device.class);
        device.setSecret(secret);
        device.setIotId(deviceId);
        device.setPhysicalLocationType(ObjectUtil.isNotNull(physicalLocationType)?physicalLocationType:-1);
        this.save(device);


    }

    @Override
    public DeviceDetailVo queryDeviceDetail(String iotId) {
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.setDeviceId(iotId);
        ShowDeviceResponse response = iotDAClient.showDevice(request);
        if (response.getHttpStatusCode() != 200){
            throw new ServiceException("查询设备详情失败");
        }
        String status = response.getStatus();
        String activeTime = response.getActiveTime();
        LocalDateTime dateTime = LocalDateTimeUtil.parse(activeTime, DatePattern.UTC_MS_PATTERN)
                .atZone(ZoneId.of("UTC")) // 明确当前时间为UTC时区
                .withZoneSameInstant(ZoneId.of("Asia/Shanghai")) // 转换为中国时区
                .toLocalDateTime();// 转回LocalDateTime（不带时区信息）
        Device device = this.lambdaQuery().eq(Device::getIotId, iotId).one();
        if (ObjectUtil.isNull(device)){
            throw new BaseException("设备不存在");
        }
        DeviceDetailVo deviceDetailVo = BeanUtil.toBean(device, DeviceDetailVo.class);
        deviceDetailVo.setDeviceStatus(status);
        deviceDetailVo.setActiveTime(dateTime);
        return deviceDetailVo;
    }

    @Override
    public List<Map<String,Object>> queryServiceProperties(String iotId) {
        //1-设置影子数据properties和event_time转化为properties+event
            ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        request.setDeviceId(iotId);
        ShowDeviceShadowResponse showDeviceShadowResponse = iotDAClient.showDeviceShadow(request);
        if (showDeviceShadowResponse.getHttpStatusCode()!=200){
            throw new HttpException("查询设备数据失败");
        }
        List<DeviceShadowData> shadow = showDeviceShadowResponse.getShadow();
        if (CollUtil.isEmpty(shadow)){
            return List.of();
        }
        DeviceShadowProperties reported = shadow.get(0).getReported();
        JSONObject properties = JSONUtil.parseObj(reported.getProperties());
        if (MapUtil.isEmpty( properties)){
            return List.of();
        }
        String eventTimeStr = reported.getEventTime();
        LocalDateTime dateTime = LocalDateTimeUtil.parse(eventTimeStr, "yyyyMMdd'T'HHmmss'Z'")
                .atZone(ZoneId.of("UTC"))
                .withZoneSameInstant(ZoneId.of("Asia/Shanghai")).toLocalDateTime();
        //2-构造Map集合
        List<Map<String,Object>> result = new ArrayList<>(properties.size());
        properties.forEach((k,v)->{
            Map<String, Object> map = MapUtil.<String, Object>builder()
                    .put("functionId", k)
                    .put("value", v)
                    .put("eventTime", dateTime).build();
            result.add(map);
        });
        return result;
    }
}
